﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using Kernel;
using ElectionOfLeaderAlg.Controllers;

namespace ElectionOfLiderAlg
{
    public class ElectionRingPhase : IStepController
    {
        private readonly Graph _graph;
        private int _step = 0;
        private int _diameter;
        // число сообщений, отправленных каждому соседу на выходе
        private Dictionary<int, int> _sended;
        // текущий минимальный отличительный признак
        private Dictionary<int, int> Vp;

        /// <summary>
        /// Создаёт контроллер для фазового алгоритма
        /// </summary>
        /// <param name="nodeCount">общее количество вершин</param>
        /// <param name="initiatorCount">количество инициаторов</param>
        /// <param name="relationCount">количество  ребер</param>
        public ElectionRingPhase(int nodeCount, int relationCount, int initiatorCount, RichTextBox rtbLog)
        {
            if (initiatorCount >= nodeCount)
                throw new Exception("Инициаторов не может быть больше чем вершин в графе");

            _graph = GraphGenerator.CreateArbitraryGraph(nodeCount, relationCount);

            Random rand = new Random();
            int colI = 0;
            int randIn = 0;
            while (colI != initiatorCount)
            {
                randIn = rand.Next(0, nodeCount - 1);
                if (!_graph.Nodes[randIn].IsInitiator)
                {
                    colI++;
                    _graph.Nodes[randIn].IsInitiator = true;
                }
            }

            _diameter = nodeCount - 1;
            _sended = new Dictionary<int, int>(_graph.Nodes.Count);
            Vp = new Dictionary<int, int>(_graph.Nodes.Count);

            foreach (var node in _graph.Nodes)
            {
                _sended.Add(node.Number, 0);
                Vp.Add(node.Number, node.Number);
                node.rtbLog = rtbLog;
                if (node.IsInitiator)
                    node.State = GraphNode.NodeState.Cand;
                else
                    node.State = GraphNode.NodeState.Sleep;
            }
        }

        public Graph NextStep()
        {
            //принимаем все сообщения, отправленные на предыдущем шаге
            _graph.Nodes.ForEach(x => x.ReceiveMessages());

            // рассылка инициаторами сообщений
            if (_step == 0)
                _graph.Nodes.Where(x => x.IsInitiator).ToList().ForEach(y =>
                {
                    foreach (var child in y.Children)
                        y.SendMessage(child, new Msg(Vp[y.Number], y.Number, MessageType.tok));
                    _sended[y.Number]++;
                });

            foreach (var node in _graph.Nodes)
            {
                while (node.GetMinRec() < _diameter && node.ReceivedMessages.Count != 0)
                {
                    Msg tmp = node.GetMessage();
                    node.Rec[tmp.sender_id.ToString()]++;
                    Vp[node.Number] = (Vp[node.Number] < tmp.number) ? Vp[node.Number] : tmp.number;

                    if (node.GetMinRec() >= _sended[node.Number] && _sended[node.Number] < _diameter)
                    {
                        foreach (var child in node.Children)
                            node.SendMessage(child, new Msg(Vp[node.Number], node.Number, MessageType.tok));
                        _sended[node.Number]++;
                    }

                }
                if (node.GetMinRec() == _diameter)
                    if (Vp[node.Number] == node.Number)
                        node.State = GraphNode.NodeState.Leader;
                    else
                        node.State = GraphNode.NodeState.Lost;
            }

            _step++;

            return _graph;
        }

        public bool IsEnd()
        {
            foreach (var node in _graph.Nodes)
            {
                if (node.State != GraphNode.NodeState.Leader && node.State != GraphNode.NodeState.Lost)
                    return false;
            }
            return true;
            //return _graph.Nodes.Any(x => x.State == GraphNode.NodeState.Leader);
        }

        public Graph GetCurrentState()
        {
            return _graph;
        }
    }
}
